package cn.icanci.loopstack.ras.server.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 注册中心的Server有向自己注册的功能
 * - 如果配置的注册中心的地址就是自己，则先进行ip进行请求
 * - 如果请求失败，则将配置的ip地址和port进行缓存，此时注册中心就只有自己
 * - 这种设计有一种弊端：如果每个服务都是注册自己，那么服务端就数据孤岛了。服务端集群则不存在
 * 
 * @author icanci
 * @since 1.0 Created in 2023/01/01 09:55
 */
@SuppressWarnings("all")
public class NamedServer {

    private static final Logger   logger = LoggerFactory.getLogger(NamedServer.class);

    private static RegisterServer registerServer;

    public static void setRegisterService(RegisterServer registerServer) {
        NamedServer.registerServer = registerServer;
    }

    public static void startClient(String serverIps, int serverPort, int clientPort) {
        // 启动时候注册
        startClient0(serverIps, serverPort, clientPort);
        // 自动进行注册
        // Tips: 项目启动了，但是没有配置项目信息，此时注册失败，如果不自动注册，则需要进行重启才能注册。因此开启自助注册
        autoRegister(serverIps, serverPort, clientPort);
    }

    private static void startClient0(String serverAddress, int serverPort, int clientPort) {
        Thread rasThread = new Thread(() -> {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();

            ServerBootstrap bootstrap = new ServerBootstrap();

            bootstrap.group(bossGroup, workerGroup) //
                .channel(NioServerSocketChannel.class) //
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel channel) throws Exception {
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast(new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS));
                        pipeline.addLast(new HttpServerCodec());
                        pipeline.addLast(new HttpObjectAggregator(5 * 1024 * 1024));
                        pipeline.addLast(new NamedNettyServerHandler());
                    }
                }).childOption(ChannelOption.SO_KEEPALIVE, true);

            try {
                ChannelFuture future = bootstrap.bind(clientPort).sync();

                doRegistry(serverAddress, serverPort, clientPort);

                future.channel().closeFuture().sync();

            } catch (InterruptedException e) {
                logger.info("RAS remoting server interruptedException", e);
            } catch (Exception e) {
                logger.info("RAS remoting server error", e);
            } finally {
                workerGroup.shutdownGracefully();
                bossGroup.shutdownGracefully();
            }

        });
        rasThread.setDaemon(true);
        rasThread.start();
    }

    /**
     * 将SDK所在服务注册到注册中心
     *
     * @param serverAddress 服务端ip地址
     * @param serverPort 服务端端口
     * @param clientPort 客户端端口
     */
    private static void doRegistry(String serverAddress, int serverPort, int clientPort) {
        registerServer.register(serverAddress, serverPort, clientPort);
    }

    /**
     * 自动注册
     *
     * @param serverAddress 服务端ip地址
     * @param serverPort 服务端端口
     * @param clientPort 客户端端口
     */
    private static void autoRegister(String serverAddress, int serverPort, int clientPort) {
        Thread autoRegisterThread = new Thread(() -> {
            // 每120秒刷新注册一次
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(120));
            doRegistry(serverAddress, serverPort, clientPort);
        });
        autoRegisterThread.setDaemon(true);
        autoRegisterThread.start();
    }
}
